/*
 * Observable.h
 *
 *  Created on: Feb 19, 2012
 *      Author: snirgaz
 */

#ifndef OBSERVABLE_H_
#define OBSERVABLE_H_
#include "def.h"
#include "ArrayMultiD.h"
#include "hdf5RW.h"
#include <map>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/matrix.hpp>

using namespace H5;
using namespace std;
using namespace boost::numeric;

struct ObsvervableData {
	typedef std::vector<double> DoubleVector;
	typedef DoubleVector::iterator DoubleVectorIter;
	DoubleVector mean;
	DoubleVectorIter meanIter;
	DoubleVector variance;
	DoubleVectorIter varianceIter;
	DoubleVector currentData;
	DoubleVectorIter currentDataIter;
	std::vector<int> loc;
	std::vector<int>::iterator locIter;
	std::vector<int> numOfMeasure;
	std::vector<int>::iterator numOfMeasureIter;
};

template<class T>
class ScalarObs {
public:
	typedef T value_type;
	typedef T mean_type;
	typedef T var_type;
	struct InitData {
		int numOfBins;
		int binSize;
	};
	void init(InitData initData) {
		mean_ = var_ = 0;
		loc_ = 0;
		numOfMeasure_ = 0;
		binSize_ = initData.binSize;
		currentData_ = 0;
	}
	bool update(mean_type &inData) {
		numOfMeasure_++;
		mean_type delta = inData - mean_;
		mean_ += delta / double(numOfMeasure_);
		if (numOfMeasure_ > 1)
			var_ += delta * (inData - mean_);
		loc_++;
		currentData_ += (1. / double(binSize_)) * (inData);
		if (loc_ == binSize_) {
			inData = currentData_;
			loc_ = 0;
			currentData_ = 0;
			return true;
		} else
			return false;

	}
	void getData(ObsvervableData &obsvervableData) const {
		obsvervableData.mean.push_back(mean_);
		obsvervableData.currentData.push_back(currentData_);
		obsvervableData.variance.push_back(var_);
		obsvervableData.loc.push_back(loc_);
		obsvervableData.numOfMeasure.push_back(numOfMeasure_);
	}
	void setData(ObsvervableData &obsvervableData) {
		mean_ = *obsvervableData.meanIter++;
		currentData_ = *obsvervableData.currentDataIter++;
		var_ = *obsvervableData.varianceIter++;
		loc_ = (*obsvervableData.locIter++);
		numOfMeasure_ = *obsvervableData.numOfMeasureIter++;
	}
	DimsHdf5 meanTypeDims() const {
		return DimsHdf5();
	}
	DimsHdf5 varTypeDims() const {
		return DimsHdf5();
	}
	int meanTotalSize() const {
		return 1;
	}
	int varTotalSize() const {
		return 1;
	}
	int getNumOfMeasure() const {
		return numOfMeasure_;
	}
private:
	int numOfMeasure_, loc_, binSize_;
	mean_type mean_, currentData_;
	var_type var_;
};

//template<class T>

template<class T>
class VectorObs {
public:
	typedef T value_type;
	typedef ublas::vector<T> mean_type;
	typedef ublas::matrix<T> var_type;
	struct InitData {
		int numOfBins;
		int numOfObs;
		int binSize;
	};
	void init(InitData initData) {
		numOfMeasure_ = 0;
		numOfObs_ = initData.numOfObs;
		mean_ = ublas::zero_vector<T>(initData.numOfObs);
		currentData_ = ublas::zero_vector<T>(initData.numOfObs);
		var_ = ublas::zero_matrix<T>(initData.numOfObs, initData.numOfObs);
		loc_ = 0;
		binSize_ = initData.binSize;
	}
	;
	bool update(mean_type &inData) {
		numOfMeasure_++;
		mean_type delta = inData - mean_;
		mean_ += delta / double(numOfMeasure_);
		if (numOfMeasure_ > 1) {
			var_ += ublas::outer_prod(delta, ublas::trans(inData - mean_));
		}
		loc_++;
		currentData_ += (1. / double(binSize_)) * inData;
		if (loc_ == binSize_) {
			inData = currentData_;
			fill(currentData_.begin(), currentData_.end(), 0.);
			loc_ = 0;
			return true;
		} else {
			return false;
		}
	}
	void setData(ObsvervableData &obsvervableData) {
		std::copy(obsvervableData.meanIter,
				obsvervableData.meanIter + numOfObs_, mean_.begin());
		obsvervableData.meanIter += numOfObs_;
		std::copy(obsvervableData.currentDataIter,
				obsvervableData.currentDataIter + numOfObs_,
				currentData_.begin());
		obsvervableData.currentDataIter += numOfObs_;
		for (auto i1 = var_.begin1(); i1 != var_.end1(); ++i1)
			for (auto i2 = i1.begin(); i2 != i1.end(); ++i2)
				*i2 = *obsvervableData.varianceIter++;
		//obsvervableData.varianceIter += numOfObs_ * numOfObs_;
		loc_ = *obsvervableData.locIter++;
		numOfMeasure_ = *obsvervableData.numOfMeasureIter++;
	}
	void getData(ObsvervableData &obsvervableData) const {
		std::copy(mean_.begin(), mean_.end(),
				std::back_inserter(obsvervableData.mean));
		std::copy(currentData_.begin(), currentData_.end(),
				std::back_inserter(obsvervableData.currentData));
		for (auto i1 = var_.begin1(); i1 != var_.end1(); ++i1)
			for (auto i2 = i1.begin(); i2 != i1.end(); ++i2)
				obsvervableData.variance.push_back(*i2);
		obsvervableData.loc.push_back(loc_);
		obsvervableData.numOfMeasure.push_back(numOfMeasure_);
	}
	DimsHdf5 meanTypeDims() const {
		return DimsHdf5(1, numOfObs_);
	}
	DimsHdf5 varTypeDims() const {
		return DimsHdf5(2, numOfObs_);
	}
	int meanTotalSize() const {
		return numOfObs_;
	}
	int varTotalSize() const {
		return numOfObs_ * numOfObs_;
	}
	int getNumOfMeasure() const {
		return numOfMeasure_;
	}
private:
	int numOfObs_, numOfMeasure_, loc_, binSize_;
	mean_type mean_, currentData_;
	var_type var_;
};

template<class Obs>
class Observable {
	typedef typename Obs::value_type value_type;
	typedef typename Obs::mean_type mean_type;
	typedef typename Obs::var_type var_type;
	typedef typename std::vector<Obs> LevelData;
	typedef typename std::vector<Obs>::iterator LevelDataIt;
public:
	typedef typename Obs::InitData InitData;
	void init(InitData initData);
	void operator<<(mean_type inData);
	void getData(ObsvervableData &obsvervableData) const;
	void setData(ObsvervableData &obsvervableData);
	void dumpToFile(hdf5RW &hdf5File, string const &dsName) const;
	void readFromFile(hdf5RW &hdf5File, string const &dsName);
	int meanTotalSize() const;
	int varTotalSize() const;
	int regularTotalSize() const;
	DimsHdf5 meanTypeDims() const;
	DimsHdf5 varTypeDims() const;
	DimsHdf5 regularDims() const;
	bool checkDone() const;
	int getNumOfMeasure() const;
private:
	int numOfLevelsMeasure_, numOfLevels_;
	int totalNumberOfMeasure_;
	LevelData levelData_;
	bool done_;
};
template<class Obs>
bool Observable<Obs>::checkDone() const {
	return (totalNumberOfMeasure_ == levelData_.begin()->getNumOfMeasure());
}
template<class Obs>
int Observable<Obs>::getNumOfMeasure() const {
	return levelData_.begin()->getNumOfMeasure();
}
template<class Obs>
int Observable<Obs>::meanTotalSize() const {
	return numOfLevelsMeasure_ * (levelData_.begin()->meanTotalSize());
}
template<class Obs>
int Observable<Obs>::varTotalSize() const {
	return numOfLevelsMeasure_ * (levelData_.begin()->varTotalSize());
}
template<class Obs>
int Observable<Obs>::regularTotalSize() const {
	return numOfLevelsMeasure_;
}
template<class Obs>
DimsHdf5 Observable<Obs>::meanTypeDims() const {
	DimsHdf5 temp = levelData_.begin()->meanTypeDims();
	temp.insert(temp.begin(), numOfLevelsMeasure_);
	return temp;
}
template<class Obs>
DimsHdf5 Observable<Obs>::varTypeDims() const {
	DimsHdf5 temp = levelData_.begin()->varTypeDims();
	temp.insert(temp.begin(), numOfLevelsMeasure_);
	return temp;
}
template<class Obs>
DimsHdf5 Observable<Obs>::regularDims() const {
	DimsHdf5 temp;
	temp.insert(temp.begin(), numOfLevelsMeasure_);
	return temp;
}
template<class Obs>
void Observable<Obs>::getData(ObsvervableData &obsvervableData) const {
	for (auto it = levelData_.begin(); it != levelData_.end(); it++) {
		it->getData(obsvervableData);
	}
}
template<class Obs>
void Observable<Obs>::setData(ObsvervableData &obsvervableData) {
	for (auto it = levelData_.begin(); it != levelData_.end(); it++) {
		it->setData(obsvervableData);
	}
}
template<class Obs>
void Observable<Obs>::operator <<(mean_type inData) {
	mean_type temp = inData;
	for (auto it = levelData_.begin(); it != levelData_.end(); it++) {
		if (!(it->update(temp)))
			break;
	}
}
template<class Obs> void Observable<Obs>::init(InitData initData) {
	numOfLevels_ = initData.numOfBins;
	totalNumberOfMeasure_ = (int) pow((double) initData.binSize, numOfLevels_);
	if (initData.numOfBins < 2) {
		std::cout << "At least 2 Bins";
		exit(1);
	}
	numOfLevelsMeasure_ = initData.numOfBins - 1;
	levelData_.resize(numOfLevelsMeasure_);
	for (LevelDataIt it = levelData_.begin(); it != levelData_.end(); it++)
		it->init(initData);
	done_ = false;
}
template<class Obs>
void Observable<Obs>::dumpToFile(hdf5RW &hdf5File, string const &dsName) const {
	try {
		// Allocate Memory
		ObsvervableData obsvervableData;
		DimsHdf5 meanDims = this->meanTypeDims();
		DimsHdf5 varDims = this->varTypeDims();
		DimsHdf5 regularDims = this->regularDims();
		int meanTotalSize = this->meanTotalSize();
		int varTotalSize = this->varTotalSize();
		obsvervableData.mean.reserve(meanTotalSize);
		obsvervableData.currentData.reserve(meanTotalSize);
		obsvervableData.variance.reserve(varTotalSize);
		obsvervableData.numOfMeasure.reserve(numOfLevelsMeasure_);
		obsvervableData.loc.reserve(numOfLevelsMeasure_);
		this->getData(obsvervableData);
		//DataSetTypeMap dataSetTypeMap = this->getHDF5Data(DimsHdf5());
		hdf5File.CreateGroup(dsName);
		hdf5File.writeComp<double>(dsName + "/Mean", meanDims,
				&(obsvervableData.mean[0]));
		hdf5File.writeComp<double>(dsName + "/CurrentData", meanDims,
				&(obsvervableData.currentData[0]));
		hdf5File.writeComp<double>(dsName + "/Variance", varDims,
				&(obsvervableData.variance[0]));
		hdf5File.writeComp<int>(dsName + "/NumOfMeasure", regularDims,
				&(obsvervableData.numOfMeasure[0]));
		hdf5File.writeComp<int>(dsName + "/Loc", regularDims,
				&(obsvervableData.loc[0]));
	} catch (DataSetIException &error) {
		error.printError();
		exit(-1);
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
	// catch failure caused by the DataSpace operations
	catch (DataSpaceIException &error) {
		error.printError();
		exit(-1);
	}
}
template<class Obs>
void Observable<Obs>::readFromFile(hdf5RW &hdf5File, string const &dsName) {
	try {
		// Allocate Memory
		ObsvervableData obsvervableData;
		int meanTotalSize = numOfLevelsMeasure_
				* levelData_.begin()->meanTotalSize();
		int varTotalSize = numOfLevelsMeasure_
				* levelData_.begin()->varTotalSize();
		obsvervableData.mean.resize(meanTotalSize);
		obsvervableData.meanIter = obsvervableData.mean.begin();
		obsvervableData.currentData.resize(meanTotalSize);
		obsvervableData.currentDataIter = obsvervableData.currentData.begin();
		obsvervableData.variance.resize(varTotalSize);
		obsvervableData.varianceIter = obsvervableData.variance.begin();
		obsvervableData.numOfMeasure.resize(numOfLevelsMeasure_);
		obsvervableData.numOfMeasureIter = obsvervableData.numOfMeasure.begin();
		obsvervableData.loc.resize(numOfLevelsMeasure_);
		obsvervableData.locIter = obsvervableData.loc.begin();
		hdf5File.read<double>(dsName + "/Mean", &(obsvervableData.mean[0]));
		hdf5File.read<double>(dsName + "/CurrentData",
				&(obsvervableData.currentData[0]));
		hdf5File.read<double>(dsName + "/Variance",
				&(obsvervableData.variance[0]));
		hdf5File.read<int>(dsName + "/NumOfMeasure",
				&(obsvervableData.numOfMeasure[0]));
		hdf5File.read<int>(dsName + "/Loc", &(obsvervableData.loc[0]));
		this->setData(obsvervableData);
	} catch (DataSetIException error) {
		error.printError();
		exit(-1);
	} catch (FileIException error) {
		error.printError();
		exit(-1);
	}
	// catch failure caused by the DataSpace operations
	catch (DataSpaceIException error) {
		error.printError();
		exit(-1);
	}
}
template<class Obs, int D>
class ArrayObservable: public ArrayMulti<Obs, D> {
	typedef typename Obs::InitData init_data;
public:
	void init(boost::array<int, D> sizes, init_data initData) {
		static_cast<ArrayMulti<Obs, D> *>(this)->init(sizes);
		for (auto it = this->begin(); it != this->end(); it++)
			it->init(initData);
	}
	void getData(ObsvervableData &obsvervableData) const;
	void setData(ObsvervableData &obsvervableData);
	void dumpToFile(hdf5RW &hdf5File, string dsName) const;
	void readFromFile(hdf5RW &hdf5File, string dsName);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
template<class Obs, int D>
void ArrayObservable<Obs, D>::getData(ObsvervableData &obsvervableData) const {
	for (auto it = this->begin(); it != this->end(); it++)
		it->getData(obsvervableData);
}
template<class Obs, int D>
void ArrayObservable<Obs, D>::setData(ObsvervableData &obsvervableData) {
	for (auto it = this->begin(); it != this->end(); it++)
		it->setData(obsvervableData);
}
template<class Obs, int D>
void ArrayObservable<Obs, D>::dumpToFile(hdf5RW &hdf5File,
		string dsName) const {
	try {
		DimsHdf5 arrayDims;
		arrayDims.insert(arrayDims.begin(), ArrayMulti<Obs, D>::sizes_.begin(),
				ArrayMulti<Obs, D>::sizes_.end());
		hdf5File.CreateGroup(dsName);
		// Allocate Memory
		ObsvervableData obsvervableData;
		DimsHdf5 meanDims = this->begin()->meanTypeDims();
		meanDims.insert(meanDims.begin(), arrayDims.begin(), arrayDims.end());
		DimsHdf5 varDims = this->begin()->varTypeDims();
		varDims.insert(varDims.begin(), arrayDims.begin(), arrayDims.end());
		DimsHdf5 regularDims = this->begin()->regularDims();
		regularDims.insert(regularDims.begin(), arrayDims.begin(),
				arrayDims.end());
		int meanTotalSize = (this->begin()->meanTotalSize())
				* this->totalSize();
		int varTotalSize = (this->begin()->varTotalSize()) * this->totalSize();
		int regularTotalSize = (this->begin()->regularTotalSize())
				* this->totalSize();
		obsvervableData.mean.reserve(meanTotalSize);
		obsvervableData.currentData.reserve(meanTotalSize);
		obsvervableData.variance.reserve(varTotalSize);
		obsvervableData.numOfMeasure.reserve(regularTotalSize);
		obsvervableData.loc.reserve(regularTotalSize);
		this->getData(obsvervableData);
		//DataSetTypeMap dataSetTypeMap = this->getHDF5Data(DimsHdf5());
		hdf5File.writeComp<double>(dsName + "/Mean", meanDims,
				&(obsvervableData.mean[0]));
		hdf5File.writeComp<double>(dsName + "/CurrentData", meanDims,
				&(obsvervableData.currentData[0]));
		hdf5File.writeComp<double>(dsName + "/Variance", varDims,
				&(obsvervableData.variance[0]));
		hdf5File.writeComp<int>(dsName + "/NumOfMeasure", regularDims,
				&(obsvervableData.numOfMeasure[0]));
		hdf5File.writeComp<int>(dsName + "/Loc", regularDims,
				&(obsvervableData.loc[0]));
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
}
template<class Obs, int D>
bool ArrayObservable<Obs, D>::checkDone() const {
	return this->begin()->checkDone();
}
template<class Obs, int D>
int ArrayObservable<Obs, D>::getNumOfMeasure() const {
	return this->begin()->getNumOfMeasure();
}
template<class Obs, int D>
void ArrayObservable<Obs, D>::readFromFile(hdf5RW &hdf5File, string dsName) {
	try {
		// Allocate Memory
		ObsvervableData obsvervableData;
		int meanTotalSize = (this->begin()->meanTotalSize())
				* this->totalSize();
		int varTotalSize = (this->begin()->varTotalSize()) * this->totalSize();
		int regularTotalSize = (this->begin()->regularTotalSize())
				* this->totalSize();
		obsvervableData.mean.resize(meanTotalSize);
		obsvervableData.meanIter = obsvervableData.mean.begin();
		obsvervableData.currentData.resize(meanTotalSize);
		obsvervableData.currentDataIter = obsvervableData.currentData.begin();
		obsvervableData.variance.resize(varTotalSize);
		obsvervableData.varianceIter = obsvervableData.variance.begin();
		obsvervableData.numOfMeasure.resize(regularTotalSize);
		obsvervableData.numOfMeasureIter = obsvervableData.numOfMeasure.begin();
		obsvervableData.loc.resize(regularTotalSize);
		obsvervableData.locIter = obsvervableData.loc.begin();
		//DataSetTypeMap dataSetTypeMap = this->getHDF5Data(DimsHdf5());
		hdf5File.read<double>(dsName + "/Mean", &(obsvervableData.mean[0]));
		hdf5File.read<double>(dsName + "/CurrentData",
				&(obsvervableData.currentData[0]));
		hdf5File.read<double>(dsName + "/Variance",
				&(obsvervableData.variance[0]));
		hdf5File.read<int>(dsName + "/NumOfMeasure",
				&(obsvervableData.numOfMeasure[0]));
		hdf5File.read<int>(dsName + "/Loc", &(obsvervableData.loc[0]));
		this->setData(obsvervableData);
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
}

// Observable Binning

struct ObsvervableDataBin {
	typedef std::vector<double> DoubleVector;
	typedef DoubleVector::iterator DoubleVectorIter;
	DoubleVector mean;
	DoubleVectorIter meanIter;
	DoubleVector var;
	DoubleVectorIter varIter;
	DoubleVector meanBins;
	DoubleVectorIter meanBinsIter;
	vector<int> numOfMeasure;
	vector<int>::iterator numOfMeasureIter;
	vector<int> cBinNum;
	vector<int>::iterator cBinNumIter;
	vector<int> numOfMeasureBin;
	vector<int>::iterator numOfMeasureBinIter;
	void dump(hdf5RW &hdf5File, string const &dsName, DimsHdf5 regularDims,
			DimsHdf5 meanBinsDims) {
		hdf5File.CreateGroup(dsName);
		hdf5File.writeComp<double>(dsName + "/Mean", regularDims, &(mean[0]));
		hdf5File.writeComp<double>(dsName + "/Variance", regularDims,
				&(var[0]));
		hdf5File.writeComp<double>(dsName + "/MeanBins", meanBinsDims,
				&(meanBins[0]));
		hdf5File.writeComp<int>(dsName + "/NumOfMeasure", regularDims,
				&(numOfMeasure[0]));
		hdf5File.writeComp<int>(dsName + "/NumOfMeasureBins", regularDims,
				&(numOfMeasureBin[0]));
		hdf5File.writeComp<int>(dsName + "/CurrentMeasureBin", regularDims,
				&(cBinNum[0]));
	}
	void read(hdf5RW &hdf5File, string const dsName) {
		hdf5File.read<double>(dsName + "/Mean", &(mean[0]));
		hdf5File.read<double>(dsName + "/MeanBins", &(meanBins[0]));
		hdf5File.read<int>(dsName + "/NumOfMeasure", &(numOfMeasure[0]));
		hdf5File.read<int>(dsName + "/NumOfMeasureBins", &(numOfMeasureBin[0]));
		hdf5File.read<int>(dsName + "/CurrentMeasureBin", &(cBinNum[0]));
	}

};

template<class T>
class ScalarBinObs {
public:
	typedef T value_type;
	typedef T mean_type;
	struct InitData {
		int numOfBins;
		int binSize;
	};
	void init(InitData initData) {
		mean_ = 0.;
		numOfMeasure_ = 0;
		numOfMeasureBin_ = 0;
		cBinNum_ = 0;
		binSize_ = initData.binSize;
		numOfBins_ = initData.numOfBins;
		binsMean_.resize(numOfBins_);
		totalNumberOfMeasure_ = binSize_ * numOfBins_;
		fill(binsMean_.begin(), binsMean_.end(), 0.);
	}
	void update(mean_type &inData) {

		if (numOfMeasureBin_ == binSize_) {
			cBinNum_++;
			numOfMeasureBin_ = 0;
		}
		numOfMeasure_++;
		numOfMeasureBin_++;
		// total Mean
		mean_type delta = inData - mean_;
		mean_ += delta / double(numOfMeasure_);
		// Bins Mean
		delta = inData - binsMean_[cBinNum_];
		binsMean_.at(cBinNum_) += delta / double(numOfMeasureBin_);
	}
	void getData(ObsvervableDataBin &obsvervableDataBin) const {
		obsvervableDataBin.mean.push_back(mean_);
		std::copy(binsMean_.begin(), binsMean_.end(),
				back_inserter(obsvervableDataBin.meanBins));
		obsvervableDataBin.numOfMeasure.push_back(numOfMeasure_);
		obsvervableDataBin.numOfMeasureBin.push_back(numOfMeasureBin_);
		obsvervableDataBin.cBinNum.push_back(cBinNum_);
		obsvervableDataBin.var.push_back(calcVariance());
	}
	void setData(ObsvervableDataBin &obsvervableDataBin) {
		mean_ = *obsvervableDataBin.meanIter++;
		copy(obsvervableDataBin.meanBinsIter,
				obsvervableDataBin.meanBinsIter + numOfBins_,
				binsMean_.begin());
		obsvervableDataBin.meanBinsIter = obsvervableDataBin.meanBinsIter
				+ numOfBins_;
		numOfMeasure_ = *obsvervableDataBin.numOfMeasureIter++;
		numOfMeasureBin_ = *obsvervableDataBin.numOfMeasureBinIter++;
		cBinNum_ = *obsvervableDataBin.cBinNumIter++;
	}
	DimsHdf5 regularTypeDims() const {
		return DimsHdf5(1, 1);
	}
	DimsHdf5 BinsTypeDims() const {
		return DimsHdf5(1, numOfBins_);
	}
	int getNumOfMeasure() const {
		return numOfMeasure_;
	}
	void operator <<(mean_type &inData) {
		update(inData);
	}
	int getNumOfBins() const {
		return numOfBins_;
	}
	bool checkDone() const {
		return (numOfMeasure_ == totalNumberOfMeasure_);
	}
	double calcVariance() const {
		namespace ba = boost::accumulators;
		// The data for which we wish to calculate statistical properties:
		std::vector<double> data;
		// The accumulator set which will calculate the properties for us:
		accVar acv;
		// Use std::for_each to accumulate the statistical properties:
		auto finalIter = binsMean_.begin();
		finalIter = finalIter + cBinNum_;
		acv = std::for_each(binsMean_.begin(), finalIter, acv);
		double var = ba::variance(acv) / double(cBinNum_);
		return var;
	}
	void dumpToFile(hdf5RW &hdf5File, string const dsName) const;
	void readFromFile(hdf5RW &hdf5File, string const dsName);
private:
	int numOfMeasure_, numOfMeasureBin_, binSize_, numOfBins_, cBinNum_,
			totalNumberOfMeasure_;
	mean_type mean_;
	vector<mean_type> binsMean_;
};

template<class T>
void ScalarBinObs<T>::dumpToFile(hdf5RW &hdf5File, string const dsName) const {
	try {
		// Allocate Memory
		ObsvervableDataBin obsvervableDataBin;
		DimsHdf5 meanBinsDims = this->BinsTypeDims();
		DimsHdf5 regularDims = this->regularTypeDims();
		obsvervableDataBin.mean.reserve(1);
		obsvervableDataBin.cBinNum.reserve(1);
		obsvervableDataBin.meanBins.reserve(numOfBins_);
		obsvervableDataBin.numOfMeasure.reserve(1);
		obsvervableDataBin.numOfMeasureBin.reserve(1);
		this->getData(obsvervableDataBin);
		obsvervableDataBin.dump(hdf5File, dsName, regularDims, meanBinsDims);
	} catch (DataSetIException &error) {
		error.printError();
		exit(-1);
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
	// catch failure caused by the DataSpace operations
	catch (DataSpaceIException &error) {
		error.printError();
		exit(-1);
	}
}
template<class T>
void ScalarBinObs<T>::readFromFile(hdf5RW &hdf5File, string const dsName) {
	try {
		// Allocate Memory
		// Allocate Memory
		ObsvervableDataBin obsvervableDataBin;
		DimsHdf5 meanBinsDims = this->BinsTypeDims();
		DimsHdf5 regularDims = this->regularTypeDims();
		obsvervableDataBin.mean.reserve(1);
		obsvervableDataBin.meanIter = obsvervableDataBin.mean.begin();
		obsvervableDataBin.cBinNum.reserve(1);
		obsvervableDataBin.cBinNumIter = obsvervableDataBin.cBinNum.begin();
		obsvervableDataBin.meanBins.reserve(getNumOfBins());
		obsvervableDataBin.meanBinsIter = obsvervableDataBin.meanBins.begin();
		obsvervableDataBin.numOfMeasure.reserve(1);
		obsvervableDataBin.numOfMeasureIter =
				obsvervableDataBin.numOfMeasure.begin();
		obsvervableDataBin.numOfMeasureBin.reserve(1);
		obsvervableDataBin.numOfMeasureBinIter =
				obsvervableDataBin.numOfMeasureBin.begin();
		obsvervableDataBin.read(hdf5File, dsName);
		this->setData(obsvervableDataBin);
	} catch (DataSetIException error) {
		error.printError();
		exit(-1);
	} catch (FileIException error) {
		error.printError();
		exit(-1);
	}
	// catch failure caused by the DataSpace operations
	catch (DataSpaceIException error) {
		error.printError();
		exit(-1);
	}
}
template<class T, int D>
class ArrayObservableBin: public ArrayMulti<ScalarBinObs<T>, D> {
	typedef typename ScalarBinObs<T>::InitData init_data;
	typedef ArrayMulti<ScalarBinObs<T>, D> array_type;
public:
	void init(boost::array<int, D> sizes, init_data initData) {
		static_cast<array_type *>(this)->init(sizes);
		for (auto it = this->begin(); it != this->end(); it++)
			it->init(initData);
	}
	void getData(ObsvervableDataBin &obsvervableDataBin) const;
	void setData(ObsvervableDataBin &obsvervableDataBin);
	void dumpToFile(hdf5RW &hdf5File, string dsName) const;
	void readFromFile(hdf5RW &hdf5File, string dsName);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
template<class T, int D>
void ArrayObservableBin<T, D>::getData(
		ObsvervableDataBin &obsvervableDataBin) const {
	for (auto it = this->begin(); it != this->end(); it++)
		it->getData(obsvervableDataBin);
}
template<class T, int D>
void ArrayObservableBin<T, D>::setData(ObsvervableDataBin &obsvervableDataBin) {
	for (auto it = this->begin(); it != this->end(); it++)
		it->setData(obsvervableDataBin);
}
template<class T, int D>
void ArrayObservableBin<T, D>::dumpToFile(hdf5RW &hdf5File,
		string dsName) const {
	try {
		DimsHdf5 arrayDims;
		arrayDims.insert(arrayDims.begin(), array_type::sizes_.begin(),
				array_type::sizes_.end());

		// Allocate Memory
		ObsvervableDataBin obsvervableDataBin;
		DimsHdf5 regularDims = arrayDims;
		DimsHdf5 meanBinsDims = array_type::begin()->BinsTypeDims();
		meanBinsDims.insert(meanBinsDims.begin(), arrayDims.begin(),
				arrayDims.end());
		int regularTotalSize = array_type::totalSize();
		int minBinsTotalSize = (array_type::begin()->getNumOfBins())
				* array_type::totalSize();
		obsvervableDataBin.mean.reserve(regularTotalSize);
		obsvervableDataBin.cBinNum.reserve(regularTotalSize);
		obsvervableDataBin.meanBins.reserve(minBinsTotalSize);
		obsvervableDataBin.numOfMeasure.reserve(regularTotalSize);
		obsvervableDataBin.numOfMeasureBin.reserve(regularTotalSize);
		this->getData(obsvervableDataBin);
		obsvervableDataBin.dump(hdf5File, dsName, regularDims, meanBinsDims);
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
}
template<class T, int D>
bool ArrayObservableBin<T, D>::checkDone() const {
	return this->begin()->checkDone();
}
template<class T, int D>
int ArrayObservableBin<T, D>::getNumOfMeasure() const {
	return this->begin()->getNumOfMeasure();
}
template<class T, int D>
void ArrayObservableBin<T, D>::readFromFile(hdf5RW &hdf5File, string dsName) {
	try {
		DimsHdf5 arrayDims;
		arrayDims.insert(arrayDims.begin(),
				ArrayMulti<ScalarBinObs<T>, D>::sizes_.begin(),
				ArrayMulti<ScalarBinObs<T>, D>::sizes_.end());
		hdf5File.CreateGroup(dsName);
		// Allocate Memory
		ObsvervableDataBin obsvervableDataBin;
		DimsHdf5 regularDims = arrayDims;
		DimsHdf5 meanBinsDims = array_type::begin()->BinsTypeDims();
		meanBinsDims.insert(meanBinsDims.begin(), meanBinsDims.begin(),
				arrayDims.end());
		int regularTotalSize = array_type::totalSize();
		int minBinsTotalSize = (array_type::begin()->getNumOfBins())
				* array_type::totalSize();
		obsvervableDataBin.mean.reserve(regularTotalSize);
		obsvervableDataBin.meanIter = obsvervableDataBin.mean.begin();
		obsvervableDataBin.cBinNum.reserve(regularTotalSize);
		obsvervableDataBin.cBinNumIter = obsvervableDataBin.cBinNum.begin();
		obsvervableDataBin.meanBins.reserve(minBinsTotalSize);
		obsvervableDataBin.meanBinsIter = obsvervableDataBin.meanBins.begin();
		obsvervableDataBin.numOfMeasure.reserve(regularTotalSize);
		obsvervableDataBin.numOfMeasureIter =
				obsvervableDataBin.numOfMeasure.begin();
		obsvervableDataBin.numOfMeasureBin.reserve(regularTotalSize);
		obsvervableDataBin.numOfMeasureBinIter =
				obsvervableDataBin.numOfMeasureBin.begin();
		obsvervableDataBin.read(hdf5File, dsName);
		this->setData(obsvervableDataBin);
	} catch (FileIException &error) {
		error.printError();
		exit(-1);
	}
}
#endif
